﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dominio;
using Servicios;
using Excepciones;
using System.Data.Objects.SqlClient;



namespace Reportes
{
    public class ReporteManagerImpl : IReporteManager
    {

        private static readonly int CANTIDAD_USUARIOS_FRECUENTES = 10;

        private static readonly int CANTIDAD_BUSQUEDAS_FRECUENTES = 5;

        public static readonly string USUARIOS_FRECUENTES = "Obtener los " + CANTIDAD_USUARIOS_FRECUENTES + " usuarios mas frecuentes";

        public static readonly string BUSQUEDAS_FRECUENTES = "Obtener las " + CANTIDAD_BUSQUEDAS_FRECUENTES + " busquedas mas realizadas";

        public static readonly string LOGS_DE_USUARIOS = "Listar logs de usuarios entre dos fechas";

        private static IList<string> reportes = new List<string>() { USUARIOS_FRECUENTES, BUSQUEDAS_FRECUENTES, LOGS_DE_USUARIOS };

        private ILogService logService = FabricaDeServicios.ObtenerInstancia().ObtenerLogService();

        private static ReporteManagerImpl instancia = new ReporteManagerImpl();

        private ReporteManagerImpl() : base() { }

        public static ReporteManagerImpl ObtenerInstancia()
        {
            return instancia;
        }

        public IList<Usuario> ObtenerUsuariosMasFrecuentes()
        {
            using (DominioContainer ctx = new DominioContainer())
            {
                IList<Usuario> usuariosFrecuentes = new List<Usuario>();
                var idsDeUsuarios = from log in ctx.Logs
                                    from usu in ctx.Usuarios
                                    where usu.Id == log.UsuarioId
                                    group log by log.UsuarioId into uniqueIds
                                    select  new { 
                                        UsuId = uniqueIds.Key
                                    };
                
                foreach (var usuarioId in idsDeUsuarios.Take(CANTIDAD_USUARIOS_FRECUENTES)) 
                {
                    Usuario usu = ctx.Usuarios.First(u => u.Id == usuarioId.UsuId);
                    usuariosFrecuentes.Add(usu);
                }
                return usuariosFrecuentes;
            }
        }

        public IList<string> ObtenerBusquedasMasRealizadas()
        {
            using (DominioContainer ctx = new DominioContainer())
            {
                IList<string> palabrasFrecuentes = new List<string>();
                var palabras = from log in ctx.Logs
                               where log.Accion == "Busqueda"
                               group log by log.Keyword 
                               into keywords
                               orderby keywords.Count() descending
                               select new
                               {
                                   Palabra = keywords.Key
                               };

                foreach (var resultado in palabras.Take(CANTIDAD_BUSQUEDAS_FRECUENTES))
                {
                    palabrasFrecuentes.Add(resultado.Palabra);
                }
                return palabrasFrecuentes;
            }
        }

        public IList<Log> ObtenerLogsDeUsuariosEntreFechas(DateTime fechaIni, DateTime fechaFin, string nombreUsuario)
        {
            using (DominioContainer ctx = new DominioContainer())
            {
                IList<Log> logs = new List<Log>();
                Usuario usu = ctx.Usuarios.First(u => u.NombreUsuario == nombreUsuario);
                if (usu == null) 
                {
                    throw new ReporteException("no existe usuario de nombre " + nombreUsuario + " en la base de datos");
                }
                foreach (Log log in ctx.Logs.Where(l => l.Fecha >= fechaIni && l.Fecha <= fechaFin && l.UsuarioId == usu.Id))
                {
                    logs.Add(log);
                }
                return logs;
            }
        }


        public IList<string> ObtenerNombreDeReportes()
        {
            return reportes;
        }

        public IDictionary<string, Type> ObtenerParametrosDeReporte(string nombreReporte)
        {
            if (nombreReporte.Equals(LOGS_DE_USUARIOS)) 
            {
                return new Dictionary<string, Type>() { { "fecha de inicio", typeof(DateTime) }, { "fecha de fin", typeof(DateTime) }, { "Usuario", typeof(string) } };
            }
            return null;
        }

        public IList<object> EjecutarReportePorNombre(string nombreReporte, IDictionary<string, object> parametros)
        {
            IList<object> resultado = new List<object>();
            if (nombreReporte.Equals(LOGS_DE_USUARIOS))
            {
                return this.validarEInvocarReporteDeLogs(parametros);
            }
            else if (nombreReporte.Equals(USUARIOS_FRECUENTES))
            {
                foreach (Usuario u in this.ObtenerUsuariosMasFrecuentes())
                {
                    UsuarioDto uDto = new UsuarioDto() { Nombre = u.Nombre, Apellido = u.Apellido, NombreUsuario = u.NombreUsuario, Email = u.Email };
                    resultado.Add(uDto);
                }
            }
            else 
            {
                foreach (string s in this.ObtenerBusquedasMasRealizadas())
                {
                    BusquedaDTO busqDTO = new BusquedaDTO() { Keyword = s};
                    resultado.Add(busqDTO);
                }       
            }
            return resultado;
        }

        private IList<object> validarEInvocarReporteDeLogs(IDictionary<string, object> parametros)
        {
            IList<object> resultado = new List<object>();
            object fechaIniObject = null;
            object fechaFinObject = null;
            parametros.TryGetValue("fecha de inicio", out fechaIniObject);
            if (fechaIniObject == null)
            {
                throw new ReporteException("fecha de inicio requerida");
            }
            if (!(fechaIniObject is DateTime))
            {
                throw new ReporteException("fecha de inicio invalida");
            }
            parametros.TryGetValue("fecha de fin", out fechaFinObject);
            if (fechaFinObject == null)
            {
                throw new ReporteException("fecha de fin requerida");
            }
            if (!(fechaFinObject is DateTime))
            {
                throw new ReporteException("fecha de fin invalida");
            }
            DateTime fechaDeInicio = (DateTime)fechaIniObject;
            DateTime fechaDeFin = (DateTime)fechaFinObject;
            if (fechaDeInicio.CompareTo(fechaDeFin) >= 0)
            {
                throw new ReporteException("fecha de fin tiene que ser mayor a la fecha de inicio");
            }
            object userName = null;
            parametros.TryGetValue("Usuario", out userName);
            if (userName == null)
            {
                throw new ReporteException("usuario es requerido");
            }
            foreach (Log l in this.ObtenerLogsDeUsuariosEntreFechas(fechaDeInicio, fechaDeFin, userName.ToString()))
            {
                resultado.Add(l);
            }
            return resultado;
        }
    }

    class UsuarioDto
    {
        public string Nombre { get; set; }
        public string Apellido { get; set; }
        public string NombreUsuario { get; set; }
        public string Email { get; set; }
    }

    class BusquedaDTO {
        public string Keyword { get; set; }
    }
}
